package xuan.lib;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/**
 * Class representing the edit log 
 *  
 * @author xuan
 */


public class EditLog {
	private Document source;//internal tree of the source document
	private Document merged;//internal tree of the merged document by 3DM
	private Document eLog;//internal tree of the edit log output by 3DM
	private List<Operation> operations = new LinkedList<Operation>(); //list stocking all operations
	private int nbOp; //number of operations 

	/**
	 * @param sFile the string path of edit log
	 * @param source the source document
	 * @param merged the merged document 
	 */
	public EditLog(String sFile, Document source, Document merged) throws Exception{		
		
		eLog = DocUtils.getNormalizedDocument(sFile);		
		this.source = source;
		this.merged = merged;	
		
		this.set();
		this.sort();
		this.setDependencies();
	}

	
	/**
	 *Read edit log file and stock all operations within a list 
	 */
	private void set() {
		Node root = eLog.getDocumentElement();
		NodeList list = root.getChildNodes();
		Element e;
		
		nbOp = list.getLength();
		
		for(int i=0;i < nbOp;i++){			
			e = (Element)list.item(i);
			operations.add(new Operation(e, source, merged));
		}
	}
	
	/**
	 *Sort operations according to their dependencies 
	 */
	private void sort() {
		Operation op1, op2;
		List<Operation> tmpList = new LinkedList<Operation>();
		int pos;
		boolean unchanged = false;
		
		//put previous operation before its dependents
		while(!unchanged){
			unchanged = true;
			for(int i=0;i<operations.size();i++){
				pos = tmpList.size();
				op1 = operations.get(i);
				for(int j=0;j<tmpList.size();j++){
					op2 = tmpList.get(j);
					if(op2.isBelongTo(op1)){
						unchanged = false;
						pos = j;
						break;
					}
				}				
				tmpList.add(pos, op1);			
			}
			
			//permute tmpList & operations
			operations.clear();
			operations.addAll(tmpList);
			tmpList.clear();
		}
		
		//put dependent operation after its last previous operation within list
		for(int i=0;i<operations.size();i++){
			op1 = operations.get(i);
			pos = tmpList.size();
			for(int j=0;j<tmpList.size();j++){
				op2 = tmpList.get(j);
				if(op1.isBelongTo(op2)){					
					pos = j+1;
				}
			}
			
			tmpList.add(pos,op1);			
		}
		
		//permute tmpList & operations
		operations.clear();
		operations.addAll(tmpList);
		tmpList.clear();
	}

	
	/**
	 * identify all dependencies among operations
	 */
	private void setDependencies() {
		Operation op1, op2;
		
		for(int i=0;i < nbOp - 1; i++){
			op1 = operations.get(i);
			for(int j=i+1;j< nbOp;j++){
				op2 = operations.get(j);			
				if(op2.isBelongTo(op1)){
					op2.addAPreviousOperation(op1);
					op2.setEnabled(false);
					op1.addADependedOperation(op2);
				}
				if(op1.isBelongTo(op2)){
					op1.addAPreviousOperation(op2);
					op1.setEnabled(false);
					op2.addADependedOperation(op1);
				}
			}
		}
	}
	
	/**
	 * @return all operations stocked in list
	 */
	public List<Operation> get() {
		return operations;
	}

	/**
	 * remove an operation
	 */
	public void remove(Operation op){
		operations.remove(op);
		nbOp--;
	}

	/**
	 * add an operation
	 */
	public void add(Operation op){
		operations.add(op);
		nbOp++;
	}
	
	/**
	 * @return the number of operations
	 */
	public int size() {
		return nbOp;
	}
	
	/**
	 * get an operation by index
	 * @param index the position index of operation in the list
	 */
	public Operation getOperation(int index){
		return operations.get(index);
	}
	
	/**
	 *print all operations 
	 */
	public void display(){
		System.out.println("Operations list : ");
		Operation op;
		for(Iterator<Operation> it=operations.iterator();it.hasNext();){
			op = (Operation) it.next();
			System.out.println(op.toString());
		}
	}

	public Iterator<Operation> iterator() {
		Iterator<Operation> it = operations.iterator();
		return it;
	}

	
	/**
	 * get the position index of one operation in list
	 * @param op operation
	 */
	public int find(Operation op) {
		return operations.indexOf((Operation)op);
	}
}
